﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Windows.Media;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;
using System.Text.RegularExpressions;
using System.Text;

namespace Colorific {

	#region Provider definition
	/// <summary>
	/// This class causes a classifier to be added to the set of classifiers.
	/// </summary>
	[Export(typeof(IClassifierProvider))]
	[ContentType("CSharp")]
	internal class ColorificProvider : IClassifierProvider {
		/// <summary>
		/// Import the classification registry to be used for getting a reference
		/// to the custom classification type later.
		/// </summary>
		[Import]
		internal IClassificationTypeRegistryService ClassificationRegistry = null; // Set via MEF

		public IClassifier GetClassifier(ITextBuffer buffer) {
			return buffer.Properties.GetOrCreateSingletonProperty<Colorific>(delegate { return new Colorific(ClassificationRegistry); });
		}
	}
	#endregion //provider def

	#region Classifier
	/// <summary>
	/// Classifier that classifies all text as an instance of the OrinaryClassifierType
	/// </summary>
	class Colorific : IClassifier {
		//IClassificationType _classificationType;
		IClassificationType methodClassificationType;
		IClassificationType variableClassificationType;

		private static readonly Regex methodRegex = new Regex("(?<method>\\w+)\\(");
		private static readonly Regex variableRegex = new Regex("\\W(?<variable>[a-z_]\\w*)");

		internal Colorific(IClassificationTypeRegistryService registry) {
			methodClassificationType = registry.GetClassificationType("Colorific-Methods");
			variableClassificationType = registry.GetClassificationType("Colorific-Variables");
			//_classificationType = registry.GetClassificationType("Colorific");
		}

		/// <summary>
		/// This method scans the given SnapshotSpan for potential matches for this classification.
		/// In this instance, it classifies everything and returns each span as a new ClassificationSpan.
		/// </summary>
		/// <param name="trackingSpan">The span currently being classified</param>
		/// <returns>A list of ClassificationSpans that represent spans identified to be of this classification</returns>
		public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span) {
			List<ClassificationSpan> result = new List<ClassificationSpan>();
			CSharpLexer lexer = new CSharpLexer();
			foreach (Token t in lexer.Scan(span.GetText())) {
				IClassificationType classificationType = null;
				switch (t.Type) {
					case TokenType.METHOD: classificationType = methodClassificationType; break;
					case TokenType.VARIABLE: classificationType = variableClassificationType; break;
					default: continue;
				}
				result.Add(new ClassificationSpan(new SnapshotSpan(span.Snapshot, new Span(span.Start + t.Pos, t.Length)), classificationType));
			}
//			result.AddRange(MethodClassifications(span));
//			result.AddRange(VariableClassifications(span));
			return result;
		}
		/*
		private IList<ClassificationSpan> MethodClassifications(SnapshotSpan span) {
			List<ClassificationSpan> result = new List<ClassificationSpan>();
			foreach (Match m in methodRegex.Matches(span.GetText())) {
				Capture c = m.Groups["method"].Captures[0];
				result.Add(new ClassificationSpan(new SnapshotSpan(span.Snapshot, new Span(span.Start + c.Index,c.Length)), _classificationType));
			}
			return result;
		}
		*/
		/*
		private IList<ClassificationSpan> VariableClassifications(SnapshotSpan span) {
			List<ClassificationSpan> result = new List<ClassificationSpan>();
			foreach (Match m in variableRegex.Matches(span.GetText())) {
				if (keywords.Contains(m.Value)) continue;
				Capture c = m.Groups["variable"].Captures[0];
				result.Add(new ClassificationSpan(new SnapshotSpan(span.Snapshot, new Span(span.Start + c.Index, c.Length)), _classificationType));
			}
			return result;
		}
		*/

#pragma warning disable 67
		// This event gets raised if a non-text change would affect the classification in some way,
		// for example typing /* would cause the classification to change in C# without directly
		// affecting the span.
		public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;
#pragma warning restore 67
	}
	#endregion //Classifier
}
